use diesel::prelude::*;
use diesel::{ExpressionMethods, PgConnection};

use crate::models::{NewTodo, NewUser, UpdateTodo, UpdateUser, User};
use crate::schema::users;
use crate::{models::Todo, schema::todo};

pub struct TodoMapper;

impl TodoMapper {
  pub fn find(conn: &mut PgConnection, id: i32) -> QueryResult<Todo> {
    todo::table.find(id).get_result::<Todo>(conn)
  }

  pub fn find_multiple(
    conn: &mut PgConnection,
    limit: i64,
  ) -> QueryResult<Vec<Todo>> {
    todo::table
      .limit(limit)
      .order(todo::id.desc())
      .load::<Todo>(conn)
  }

  pub fn create(
    conn: &mut PgConnection,
    new_todo: NewTodo,
  ) -> QueryResult<Todo> {
    diesel::insert_into(todo::table)
      .values(new_todo)
      .get_result(conn)
  }

  pub fn update(
    conn: &mut PgConnection,
    id: i32,
    update_todo: UpdateTodo,
  ) -> QueryResult<Todo> {
    diesel::update(todo::table.find(id))
      .set((
        todo::email.eq(update_todo.email.to_owned()),
        todo::name.eq(update_todo.name.to_owned()),
      ))
      .execute(conn)?;

    Self::find(conn, id)
  }

  pub fn delete(conn: &mut PgConnection, id: i32) -> QueryResult<usize> {
    diesel::delete(todo::table.find(id)).execute(conn)
  }
}

pub struct UserMapper;

impl UserMapper {
  pub fn find(conn: &mut PgConnection, id: i32) -> QueryResult<User> {
    users::table.find(id).get_result::<User>(conn)
  }

  pub fn find_multiple(
    conn: &mut PgConnection,
    limit: i64,
  ) -> QueryResult<Vec<User>> {
    users::table
      .limit(limit)
      .order(users::id.desc())
      .load::<User>(conn)
  }

  pub fn create(
    conn: &mut PgConnection,
    new_user: NewUser,
  ) -> QueryResult<User> {
    diesel::insert_into(users::table)
      .values(new_user)
      .get_result(conn)
  }

  pub fn update(
    conn: &mut PgConnection,
    id: i32,
    update_user: UpdateUser,
  ) -> QueryResult<User> {
    diesel::update(users::table.find(id))
      .set((
        users::todo_id.eq(update_user.todo_id.to_owned()),
        users::code.eq(update_user.code.to_owned()),
        users::name.eq(update_user.name.to_owned()),
        users::version.eq(update_user.version.to_owned()),
        users::description.eq(update_user.description.to_owned()),
      ))
      .execute(conn)?;

    Self::find(conn, id)
  }

  pub fn delete(conn: &mut PgConnection, id: i32) -> QueryResult<usize> {
    diesel::delete(users::table.find(id)).execute(conn)
  }
}
